package com.link.base.base.user.service;

import com.alibaba.fastjson.JSONObject;
import com.link.base.base.user.dao.mybatis.mapper.ThirdLoginMapper;
import com.link.base.base.user.dao.mybatis.mapper.UserMapper;
import com.link.base.base.user.model.RespDTO;
import com.link.base.base.user.model.ThirdLogin;
import com.link.base.base.user.model.ThirdLoginDTO;
import com.link.base.base.user.model.User;
import com.link.base.user.model.CoreUser;
import com.link.core.basic.dao.mapper.BasicMapper;
import com.link.core.basic.model.BasicModel;
import com.link.core.basic.query.Filter;
import com.link.core.basic.query.QueryParams;
import com.link.core.basic.service.BasicServiceImpl;
import com.link.core.basic.service.KeyGenerateService;
import com.link.core.basic.service.ServiceException;
import com.link.core.cllog.LCLogger;
import com.link.core.util.*;
import com.link.core.util.redisclient.LinkRedisClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * QQ && 微信登录
 *
 * @author 曲元涛
 * @date 2019/03/29 17:19
 */
@Service
public class ThirdLoginServiceImpl extends BasicServiceImpl<ThirdLogin> implements ThirdLoginService, InitializingBean {

    private static final Logger logger = LoggerFactory.getLogger(ThirdLoginServiceImpl.class);

    @Resource
    private KeyGenerateService keyGenerateService;
    @Resource
    private ThirdLoginMapper thirdLoginMapper;
    @Resource
    private UserMapper userMapper;
    @Resource
    private UserService userService;
    @Resource
    private LoginLogService loginLogService;

    /**appType-loginType映射*/
    private final Map<String, String> appLoginTypeMap = new HashMap<>(4);
    /**社交平台-认证URL前缀映射*/
    private final Map<String, String> authUrlPrefixMap = new HashMap<>(2);
    /**社交平台-AppId映射*/
    private final Map<String, String> webAppIdMap = new HashMap<>(2);
    /**社交平台-绑定接口地址映射*/
    private final Map<String, String> bindRedirectMap = new HashMap<>(2);
    /**社交平台-登录接口地址映射*/
    private final Map<String, String> loginRedirectMap = new HashMap<>(2);
    /**微信appType-appId和secretKey映射*/
    private final MultiValueMap<String, String> wxAppTypeToSecret = new LinkedMultiValueMap<>(8);

    @Override
    public BasicMapper<ThirdLogin> getBasicMapper() {
        return thirdLoginMapper;
    }

    @Override
    @SuppressWarnings("rawtypes")
    public RespDTO login(ThirdLoginDTO dto, HttpServletRequest request) throws Exception {
        String token = notBlank(dto.getToken(), "请传入用户Token");
        String platformType = notBlank(dto.getPlatformType(), "请传入平台类型");
        String username = notBlank(dto.getUsername(), "请传入登录用户名");
        String appType = notBlank(dto.getAppType(), "请传入应用类型");
        if (!Arrays.asList(PLATFORM_WX, PLATFORM_QQ).contains(platformType)) {
            throw new ServiceException("USER-046");
        }
        // 获取扫码缓存的unionId
        String unionId = this.hGetAfterDelIfNecessary(token, "unionid", true);
        if (!appLoginTypeMap.containsKey(appType)) {
            throw new ServiceException("未知的第三方登录类型");
        }
        String loginType = appLoginTypeMap.get(appType);
        RespDTO result = userService.login(username, unionId + "_" + platformType, loginType, request, "");
        loginLogService.addLoginLogByRespDTO(result, request);
        return result;
    }

    @Override
    public String getWeChatAuthUrl(ThirdLoginDTO thirdLoginDTO) {
        EnumMap<AuthUrlParam, String> authParams = this.getAuthUrl(thirdLoginDTO, PLATFORM_WX);
        String redirectUri = authParams.get(AuthUrlParam.REDIRECT_URI);
        if (StringUtils.isBlank(redirectUri)) {
            return "";
        }
        String prefix = authParams.get(AuthUrlParam.AUTH_URL_PREFIX);
        String appId = authParams.get(AuthUrlParam.WEB_APP_ID);
        String state = authParams.get(AuthUrlParam.STATE);
        return prefix + "?appid=" + appId
                + "&redirect_uri=" + redirectUri
                + "&response_type=code&scope=snsapi_login&state=" + state
                + "#wechat_redirect";
    }

    @Override
    public String getQQAuthUrl(ThirdLoginDTO dto) {
        EnumMap<AuthUrlParam, String> authParams = this.getAuthUrl(dto, PLATFORM_QQ);
        String redirectUri = authParams.get(AuthUrlParam.REDIRECT_URI);
        if (StringUtils.isBlank(redirectUri)) {
            return "";
        }
        String prefix = authParams.get(AuthUrlParam.AUTH_URL_PREFIX);
        String appId = authParams.get(AuthUrlParam.WEB_APP_ID);
        String state = authParams.get(AuthUrlParam.STATE);
        return prefix + "?response_type=code&client_id=" + appId + "&redirect_uri=" + redirectUri + "&state=" + state;
    }

    @Override
    public Map<String, Object> thirdPartyBinding(ThirdLogin thirdLogin) throws Exception {
        if (StringUtils.isNull(thirdLogin.getAppType())) {
            throw new ServiceException("USER-047");
        }
        CoreUser loginUser = UserUtil.getUser();
        QueryParams qps = new QueryParams();
        String[] appTypeArr = this.getAppTypeArray(thirdLogin.getAppType());
        qps.addFilter(new Filter("userId", Filter.OP_EQUAL, String.valueOf(loginUser.getId())));
        qps.addFilter(new Filter("appType", Filter.OP_IN, Arrays.toString(appTypeArr)));

        ThirdLogin queryData = new ThirdLogin();
        BasicModel.transformClass(queryData, qps);
        queryData.setPageFlag(false);
        List<ThirdLogin> thirdLogins = thirdLoginMapper.queryByExamplePage(queryData);

        long bindQQ = thirdLogins.stream()
                .filter(e -> PLATFORM_QQ.equals(e.getPlatformType()))
                .count();
        long bindWX = thirdLogins.stream()
                .filter(e -> PLATFORM_WX.equals(e.getPlatformType()))
                .count();

        Map<String, Object> bindResult = new HashMap<>();
        bindResult.put(PLATFORM_QQ, bindQQ > 0 ? "Y" : "N");
        bindResult.put(PLATFORM_WX, bindWX > 0 ? "Y" : "N");
        return bindResult;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public JSONObject bindWx(String state, String code, String appType) throws Exception {
        JSONObject tokenObject = this.getWXAuthInfo(code, state, appType);
        String unionid = tokenObject.getString("unionid");
        String openid = tokenObject.getString("openid");
        CoreUser loginUser = UserUtil.getUser();
        if (null == loginUser) {
            throw new ServiceException("PUBLIC-027");
        }
        Long userId = loginUser.getId();
        Long corpId = loginUser.getCorpid();
        this.bindWx(unionid, openid, appType, PLATFORM_WX, userId, corpId);
        return tokenObject;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bindWx(String unionId,
                       String openId,
                       String appType,
                       String platformType,
                       Long userId,
                       Long corpId) throws Exception {
        User user = new User();
        user.setId(userId);
        user.setCorpid(corpId);
        user.setLoginCorpId(corpId);
        user = userMapper.queryById(user);
        if (null == user) {
            throw new ServiceException("USER-048");
        }
        if (StringUtils.isNotBlank(user.getWxUnionId()) && !user.getWxUnionId().equals(unionId)) {
            throw new ServiceException("USER-049");
        }
        // 绑定记录写入中间表
        ThirdLogin entity = new ThirdLogin(userId, openId, appType, PLATFORM_WX);
        entity.setId(keyGenerateService.keyGenerate());
        entity.setCreatedBy(userId);
        entity.setLastUpdatedBy(userId);
        entity.setCorpid(corpId);
        Integer affectRow = thirdLoginMapper.insertInter(entity);
        if (affectRow <= 0) {
            throw new ServiceException("USER-050");
        }
        // 更新用户WxUnionId字段
        User loginUser = new User();
        loginUser.setId(userId);
        loginUser.setWxUnionId(unionId);
        affectRow = userMapper.thirdClientBindUser(loginUser);
        if (affectRow <= 0) {
            throw new ServiceException("USER-050");
        }
    }

    @Override
    public JSONObject bindQQ(ThirdLoginDTO dto) throws Exception {
        String appType = dto.getAppType();
        JSONObject authInfo = this.getQQAuthInfo(dto.getToken());
        CoreUser user = UserUtil.getUser();
        if (null == user) {
            throw new ServiceException("PUBLIC-027");
        }
        Long userId = user.getId();
        Long corpId = user.getCorpid();
        String unionId = authInfo.getString("unionid");
        this.bindQQ(unionId, authInfo.getString("openid"), appType, PLATFORM_QQ, userId, corpId);
        return authInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bindQQ(String unionId,
                       String openId,
                       String appType,
                       String platformType,
                       Long userId,
                       Long corpId) throws Exception {
        User user = new User();
        user.setId(userId);
        user.setLoginCorpId(corpId);
        user = userMapper.queryById(user);
        if (StringUtils.isNotBlank(user.getQqUnionId()) && !user.getQqUnionId().equals(unionId)) {
            throw new ServiceException("USER-051");
        }
        user.setQqUnionId(unionId);
        user.setWxUnionId(null);
        ThirdLogin thirdLogin = new ThirdLogin(userId, openId, appType, platformType);
        thirdLogin.setId(keyGenerateService.keyGenerate());
        thirdLogin.setCreatedBy(userId);
        thirdLogin.setLastUpdatedBy(userId);
        thirdLogin.setCorpid(corpId);
        thirdLoginMapper.insertInter(thirdLogin);
        userMapper.thirdClientBindUser(user);
    }

    @Override
    public JSONObject getQQAuthToken(String code, String state, boolean isLogin) throws ServiceException {
        boolean expired = stateExpired(state);
        if (expired) {
            throw new ServiceException("USER-052");
        }
        String qqTokenUrl = RedisUtil.getProperty("qqAuthToken", CORP_ID.toString());
        String qqTokenPatam = this.getQQAuthTokenParam(code, isLogin);
        String tokenResult = HttpUtils.sendGet(qqTokenUrl, qqTokenPatam);
        tokenResult = resultStringFormat(tokenResult);
        if (StringUtils.isBlank(tokenResult)) {
            throw new ServiceException("USER-053");
        }
        JSONObject result = new JSONObject();
        String[] tokenStr = tokenResult.split("&");
        if (tokenStr.length == 3) {
            for (int i = 0; i < 3; i++) {
                String[] value = tokenStr[i].split("=");
                result.put(value[0], value[1]);
            }
        } else {
            JSONObject jsonTokenResult = JSONObject.parseObject(tokenResult);
            if (StringUtils.isNotBlank(jsonTokenResult.getString("error"))) {
                throw new ServiceException(jsonTokenResult.getString("error_description"));
            }
        }
        return result;
    }

    @Override
    public JSONObject getQQAuthInfo(String token) throws ServiceException {
        String qqInfoUrl = RedisUtil.getProperty("qqInfoUrl", CORP_ID.toString());
        String qqInfoParam = "access_token=" + token + "&unionid=1";
        String infoResult = HttpUtils.sendGet(qqInfoUrl, qqInfoParam);
        infoResult = resultStringFormat(infoResult);
        if (StringUtils.isBlank(infoResult)) {
            throw new ServiceException("USER-054");
        }
        JSONObject jsonInfoResult = JSONObject.parseObject(infoResult);
        if (StringUtils.isNotBlank(jsonInfoResult.getString("error"))) {
            throw new ServiceException(jsonInfoResult.getString("error_description"));
        }
        JSONObject result = new JSONObject();
        result.put("unionid", jsonInfoResult.getString("unionid"));
        result.put("openid", jsonInfoResult.getString("openid"));
        result.put("access_token", token);
        return result;
    }

    @Override
    public JSONObject getWXAuthInfo(String code, String state, String appType) throws ServiceException {
        if (StringUtils.isBlank(code)) {
            throw new ServiceException("USER-055");
        }
        if (StringUtils.isBlank(appType)) {
            throw new ServiceException("USER-047");
        }
        if (Arrays.asList(PC_CRM, PC_DMS, PC_STORE).contains(appType)) {
            if (StringUtils.isBlank(state)) {
                throw new ServiceException("USER-056");
            }
            if (stateExpired(state)) {
                throw new ServiceException("USER-057");
            }
        }
        if (!wxAppTypeToSecret.containsKey(appType)) {
            throw new ServiceException("USER-058");
        }
        String appId = wxAppTypeToSecret.getFirst(appType);
        String secret = wxAppTypeToSecret.get(appType).get(1);
        String param = "appid=" + appId + "&secret=" + secret + "&code=" + code + "&grant_type=authorization_code";
        String accessTokenUrl = RedisUtil.getProperty("wxAuthToken", CORP_ID.toString());

        String resultParams = HttpUtils.sendGet(accessTokenUrl, param);
        JSONObject tokenObject = JSONObject.parseObject(resultParams);
        if (null == tokenObject) {
            throw new ServiceException("USER-059");
        }
        if (tokenObject.containsKey(ERR_CODE)) {
            throw new ServiceException(tokenObject.getString(ERR_MSG));
        }
        return tokenObject;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unBoundThirdParty(String appType, String platformType) throws Exception {
        Long userId = UserUtil.getUser().getId();
        Map<String, String> unionMap = this.getUnionAndOpenId(appType, platformType, userId);
        String unionId = unionMap.get("unionId");
        String openId = unionMap.get("openId");

        String[] appTypeArr = this.getAppTypeArray(appType);
        ThirdLogin thirdLogin = new ThirdLogin(userId, platformType, Arrays.asList(appTypeArr));
        Integer updateStatus = thirdLoginMapper.unboundThirdAccount(thirdLogin);
        if (updateStatus <= 0) {
            throw new ServiceException("USER-050");
        }
        ThirdLogin entity = new ThirdLogin(userId);
        List<ThirdLogin> thirdLogins = thirdLoginMapper.queryAll(entity);
        long qqExists = thirdLogins.stream()
                .filter(e -> PLATFORM_QQ.equals(e.getPlatformType()))
                .count();
        long wxExists = thirdLogins.stream()
                .filter(e -> PLATFORM_WX.equals(e.getPlatformType()))
                .count();
        User user = new User();
        user.setId(userId);
        if (qqExists == 0 && PLATFORM_QQ.equals(platformType)) {
            updateStatus = userMapper.unBoundQQWithOutUnionid(user);
            if (updateStatus <= 0) {
                throw new ServiceException("USER-050");
            }
        }
        if (wxExists == 0 && PLATFORM_WX.equals(platformType)) {
            updateStatus = userMapper.unBoundWXWithOutUnionid(user);
            if (updateStatus <= 0) {
                throw new ServiceException("USER-050");
            }
        }
        //解绑成功，删除redis缓存
        String redisKey = unionId + "_" + openId;
        LinkRedisClient j = null;
        try {
            j = RedisUtil.getJedis();
            j.del(redisKey);
        } finally {
            RedisUtil.returnResource(j);
        }
    }

    @Override
    public List<User> getUsersByUnionIdAndAppType(String appType, String token, String platform, boolean wxSecAuth) throws ServiceException {
        String unionId;
        if (Arrays.asList(PC_CRM, PC_DMS, PC_STORE).contains(appType)) {
            // PC端获取UnionId
            unionId = this.getAuthInfoFromRedis(token).get("unionid");
        } else if (PLATFORM_QQ.equals(platform)) {
            // 移动端QQ登录获取UnionId
            unionId = this.getQQAuthInfo(token).getString("unionid");
        } else if (PLATFORM_WX.equals(platform) && wxSecAuth) {
            // 微信避开二次授权请求，前端传值UnionId到token字段
            unionId = token;
        } else {
            // 微信授权请求，前端传值存放认证信息redisKey到token字段
            unionId = this.getAuthInfoFromRedis(token).get("unionid");
        }
        notBlank(unionId, "获取unionid失败");

        User user = new User();
        if (PLATFORM_QQ.equals(platform)) {
            user.setQqUnionId(unionId);
        } else if (PLATFORM_WX.equals(platform)) {
            user.setWxUnionId(unionId);
        } else {
            throw new ServiceException("USER-046");
        }
        List<User> result = new ArrayList<>();
        try {
            List<User> list = userMapper.getUsersByUnionId(user);
            switch (appType) {
                case PC_CRM:
                    result = list.stream().filter(item -> Arrays.asList("INTER_EMP", "VendorEmployee").contains(item.getEmpType()))
                            .collect(Collectors.toList());
                    break;
                case PC_STORE:
                case APP_MARKETING:
                    result = list.stream().filter(item -> "TerminalEmployee".equals(item.getEmpType())).collect(Collectors.toList());
                    break;
                case PC_DMS:
                case APP_DMS:
                    result = list.stream().filter(item -> "DealerEmployee".equals(item.getEmpType())).collect(Collectors.toList());
                    break;
                default:
                    throw new ServiceException("USER-058");
            }

        } catch (Exception e) {
            LCLogger.error().withMessage(e.getMessage()).flush();
        }
        return result;
    }

    @Override
    public String getLoginRedirectParam(String code, String state, boolean isLogin) {
        String param;
        LinkRedisClient jedis = null;
        try {
            jedis = RedisUtil.getJedis();
            JSONObject tokenObj = this.getQQAuthToken(code, state, isLogin);
            String accessToken = tokenObj.getString("access_token");
            String refreshToken = tokenObj.getString("refresh_token");
            JSONObject authInfo = this.getQQAuthInfo(accessToken);
            String unionId = authInfo.getString("unionid");
            String openId = authInfo.getString("openid");
            String key = UUID.randomUUID().toString();
            jedis.hset(key, "access_token", accessToken);
            jedis.hset(key, "refresh_token", refreshToken);
            jedis.hset(key, "unionid", unionId);
            jedis.hset(key, "openId", openId);
            jedis.expire(key, TEN_MINUTES);
            param = "token=" + key;
        } catch (ServiceException bse) {
            param = "error=" + bse.getMessage();
        } catch (Exception e) {
            param = "error=unknown_error";
        } finally {
            RedisUtil.returnResource(jedis);
        }
        return param;
    }

    @Override
    public String getWXRandomToken(String code, String state, String appType) {
        JSONObject tokenObject = this.getWXAuthInfo(code, state, appType);
        String token = UUID.randomUUID().toString().replace("-", "");
        LinkRedisClient jedis = null;
        try {
            jedis = RedisUtil.getJedis();
            jedis.hset(token, "access_token", tokenObject.getString("access_token"));
            jedis.hset(token, "expires_in", tokenObject.getString("expires_in"));
            jedis.hset(token, "refresh_token", tokenObject.getString("refresh_token"));
            jedis.hset(token, "openId", tokenObject.getString("openid"));
            jedis.hset(token, "scope", tokenObject.getString("scope"));
            jedis.hset(token, "unionid", tokenObject.getString("unionid"));
            jedis.expire(token, TEN_MINUTES);
        } finally {
            RedisUtil.returnResource(jedis);
        }
        return token;
    }

    @Override
    public void saveThirdInfo(String unionId, String openId, String accessToken, String refreshToken, String platformType) {
        if (StringUtils.isBlank(unionId)) {
            throw new ServiceException("USER-038");
        }
        if (StringUtils.isBlank(openId)) {
            throw new ServiceException("USER-039");
        }
        if (StringUtils.isBlank(accessToken)) {
            throw new ServiceException("USER-061");
        }
        if (StringUtils.isBlank(refreshToken) && PLATFORM_WX.equals(platformType)) {
            throw new ServiceException("USER-062");
        }
        LinkRedisClient jedis = null;
        try {
            jedis = RedisUtil.getJedis();
            String redisKey = unionId + "_" + openId;
            String refreshTime = DateUtil.dateToStr(new Date(), "yyyy-MM-dd HH:mm:ss");
            boolean existed = jedis.exists(redisKey);
            if (!existed) {
                jedis.hset(redisKey, "accessToken", accessToken);
                //QQ没有refreshToken
                if (PLATFORM_WX.equals(platformType)) {
                    jedis.hset(redisKey, "refreshToken", refreshToken);
                    jedis.hset(redisKey, "refreshTime", refreshTime);
                }
                //设置过期时间30天
                jedis.expire(redisKey, 30 * 86400);
            }
        } finally {
            RedisUtil.returnResource(jedis);
        }
    }

    @Override
    public boolean wxSecAuth(String unionId, String openId, String accessToken, String appType) {
        LinkRedisClient j = null;
        try {
            String redisKey = unionId + "_" + openId;
            j = RedisUtil.getJedis();
            boolean existed = j.exists(redisKey);
            if (!existed) {
                // redis中未找到数据或refresh_token已过期
                return false;
            }
            String accessTokenRedis = j.hget(redisKey, "accessToken");
            String refreshToken = j.hget(redisKey, "refreshToken");
            String refreshTime = j.hget(redisKey, "refreshTime");
            if (!accessToken.equals(accessTokenRedis)) {
                //前端和缓存中的access_token不一致
                j.del(redisKey);
                return false;
            }
            //通过refresh_time判断access_token是否过期
            Date oldDate = DateUtil.stringToDate(refreshTime, "yyyy-MM-dd HH:mm:ss");
            Date nowDate = new Date();
            boolean accessTokenExpired = (oldDate.getTime() - nowDate.getTime()) / 1000 > 7200;
            //判断access_token是否有效
            boolean accessTokenValid = this.wxAuthAccessToken(accessToken, openId);
            if (!accessTokenValid || accessTokenExpired) {
                //刷新access_token更新到redis
                String appId;
                switch (appType) {
                    case APP_MARKETING:
                        appId = RedisUtil.getProperty("wxMobileMarketingAppId", CORP_ID.toString());
                        break;
                    case APP_DMS:
                        appId = RedisUtil.getProperty("wxMobileDMSAppId", CORP_ID.toString());
                        break;
                    default:
                        throw new ServiceException("USER-058");
                }
                JSONObject refreshObj = this.wxRefreshAccessToken(appId, refreshToken);
                String accessTokenNew = refreshObj.getString("access_token");
                String refreshTimeNew = DateUtil.dateToStr(new Date(), "yyyy-MM-dd HH:mm:ss");
                j.hset(redisKey, "accessToken", accessTokenNew);
                j.hset(redisKey, "refreshTime", refreshTimeNew);
                return true;
            }
        } finally {
            RedisUtil.returnResource(j);
        }
        return true;
    }

    @Override
    public Map<String, String> getAuthInfoFromRedis(String token) {
        Map<String, String> authInfo;
        if (StringUtils.isBlank(token)) {
            throw new ServiceException("USER-033");
        }
        LinkRedisClient jedis = null;
        try {
            jedis = RedisUtil.getJedis();
            boolean unExpired = jedis.exists(token);
            if (!unExpired) {
                throw new ServiceException("USER-063");
            }
            authInfo = jedis.hgetAll(token);
            if (authInfo.isEmpty()) {
                throw new ServiceException("USER-064");
            }
        } finally {
            RedisUtil.returnResource(jedis);
        }
        return authInfo;
    }

    @Override
    public boolean wxAuthAccessToken(String accessToken, String openId) {
        if (StringUtils.isBlank(accessToken)) {
            throw new ServiceException("USER-061");
        }
        if (StringUtils.isBlank(openId)) {
            throw new ServiceException("USER-039");
        }
        String authUri = RedisUtil.getProperty("wxAccessTokenAuthUri", CORP_ID.toString());
        String authParam = "access_token=" + accessToken + "&openid=" + openId;
        String authResult = HttpUtils.sendGet(authUri, authParam);
        if (StringUtils.isBlank(authResult)) {
            throw new ServiceException("USER-065");
        }
        JSONObject authObject = JSONObject.parseObject(authResult);
        return VALID_ACCESS_CODE.equals(authObject.getInteger("errcode"));
    }

    @Override
    public JSONObject wxRefreshAccessToken(String appId, String refreshToken) {
        if (StringUtils.isBlank(appId)) {
            throw new ServiceException("USER-066");
        }
        if (StringUtils.isBlank(refreshToken)) {
            throw new ServiceException("USER-062");
        }
        String refreshUri = RedisUtil.getProperty("wxRefreshToken", CORP_ID.toString());
        String refreshParam = "appid=" + appId + "&grant_type=refresh_token&refresh_token=" + refreshToken;
        LCLogger.info()
                .withMessageKey("微信二次授权refreshToken参数")
                .withMessage(refreshUri + "?" + refreshParam)
                .flush();
        String refreshResult = HttpUtils.sendGet(refreshUri, refreshParam);
        if (StringUtils.isBlank(refreshResult)) {
            throw new ServiceException("USER-065");
        }
        JSONObject refreshObj = JSONObject.parseObject(refreshResult);
        if (refreshObj.containsKey(ERR_CODE)) {
            LCLogger.error()
                    .withMessageKey("微信二次授权refreshToken失败")
                    .withMessage(refreshObj.getString(ERR_MSG))
                    .flush();
            throw new ServiceException(refreshObj.getString(ERR_MSG));
        }
        return refreshObj;
    }

    @Override
    public Map<String, Object> authControl(String... platformTypeArr) {
        Map<String, Object> authRes = new HashMap<>(2);
        for (String platformType : platformTypeArr) {
            switch (platformType) {
                case PLATFORM_QQ:
                    authRes.put(platformType, true);
                    String qqAuth = RedisUtil.getProperty("qqAuthFlag", CORP_ID.toString());
                    if (!Y_STR.equals(qqAuth)) {
                        authRes.put(platformType, "未开通QQ登录权限，请联系管理员");
                    }
                    break;
                case PLATFORM_WX:
                    authRes.put(platformType, true);
                    String wxAuth = RedisUtil.getProperty("wxAuthFlag", CORP_ID.toString());
                    if (!Y_STR.equals(wxAuth)) {
                        authRes.put(platformType, "未开通微信登录权限，请联系管理员");
                    }
                    break;
                default:
                    authRes.put(platformType, "未识别的平台类型");
            }
        }
        return authRes;
    }

    /**
     * 判断state参数是否过期
     *
     * @author quyuantao
     * @param state 校验是否过期字符串
     * @date 2019/4/2 20:00
     */
    private boolean stateExpired(String state) {
        LinkRedisClient j = null;
        try {
            j = RedisUtil.getJedis();
            boolean exist = j.exists(state);
            j.del(state);
            return !exist;
        } finally {
            RedisUtil.returnResource(j);
        }
    }

    /**
     * 将返回数据字符串处理为JSON格式
     *
     * @author cuizhiyong
     * @param str 待处理字符串
     * @date 2019/4/2 20:01
     */
    private String resultStringFormat(String str) {
        if (StringUtils.isBlank(str)) {
            return "";
        }
        System.out.println(str);
        int start = str.indexOf('(');
        int end = str.indexOf(')');
        return str.substring(start < 0 ? 0 : start + 1,
                end > str.length() || end <= 0 ? str.length() : end);
    }

    /**
     * 通过appType platformType userId 获取 unionId openId
     *
     * @author quyuantao
     * @date 2019/4/11 16:50
     * @param userId 用户id
     * @param appType 应用类型
     * @param platformType 平台类型
     */
    private Map<String, String> getUnionAndOpenId(String appType, String platformType, Long userId) throws Exception {
        if (StringUtils.isBlank(appType)) {
            throw new ServiceException("USER-036");
        }
        if (StringUtils.isBlank(platformType)) {
            throw new ServiceException("USER-034");
        }
        Map<String, String> paramMap = new HashMap<>();
        String unionId;
        String openId;
        CoreUser loginUser = UserUtil.getUser();

        ThirdLogin thirdLogin = new ThirdLogin();
        thirdLogin.setUserId(userId);
        String[] appTypeArr = this.getAppTypeArray(appType);
        QueryParams qps = new QueryParams();
        qps.addFilter(new Filter("appType", Filter.OP_IN, Arrays.toString(appTypeArr)));
        BasicModel.transformClass(thirdLogin, qps);
        List<ThirdLogin> list = thirdLoginMapper.queryAll(thirdLogin);
        if (list.isEmpty()) {
            throw new ServiceException("USER-106");
        }
        openId = list.get(0).getOpenId();

        User user = new User();
        user.setId(userId);
        user.setCorpid(loginUser.getCorpid());
        user.setLoginCorpId(loginUser.getLoginCorpId());
        user = userMapper.queryById(user);
        if (PLATFORM_QQ.equals(platformType)) {
            unionId = user.getQqUnionId();
        } else if (PLATFORM_WX.equals(platformType)) {
            unionId = user.getWxUnionId();
        } else {
            throw new ServiceException("USER-067");
        }

        if (StringUtils.isBlank(unionId)) {
            throw new ServiceException("USER-068");
        }
        if (StringUtils.isBlank(openId)) {
            throw new ServiceException("USER-069");
        }
        paramMap.put("unionId", unionId);
        paramMap.put("openId", openId);
        return paramMap;
    }

    /**
     * 根据应用类型返回应用类型数组
     *
     * @author quyuantao
     * @date 2019/4/16 20:32
     * @param appType 应用类型
     */
    private String[] getAppTypeArray(String appType) {
        if (StringUtils.isBlank(appType)) {
            throw new ServiceException("USER-036");
        }
        String[] appTypeArr;
        switch (appType) {
            case PC_CRM:
                appTypeArr = new String[]{PC_CRM};
                break;
            case PC_DMS:
            case APP_DMS:
                appTypeArr = new String[]{PC_DMS, APP_DMS};
                break;
            case PC_STORE:
            case APP_MARKETING:
                appTypeArr = new String[]{PC_STORE, APP_MARKETING};
                break;
            default:
                throw new ServiceException("USER-046");
        }
        return appTypeArr;
    }

    /**
     * 获取社交登录认证URL
     *
     * @author 曲元涛
     * @date 2020/7/10 14:42
     * @param dto dto       对象
     * @param platformType  平台类型
     */
    private EnumMap<AuthUrlParam, String> getAuthUrl(ThirdLoginDTO dto, String platformType) {
        String appIdKey = webAppIdMap.get(platformType);
        String prefixKey = authUrlPrefixMap.get(platformType);
        // 操作类型
        String operate = dto.getState();
        // 跳转接口URI
        String redirectUri = "";
        try {
            if ("bind".equals(operate)) {
                String bindRedirectKey = bindRedirectMap.get(platformType);
                redirectUri = URLEncoder.encode(RedisUtil.getProperty(bindRedirectKey, CORP_ID.toString()), ENCODE);
            }
            if ("login".equals(operate)) {
                String loginRedirectKey = loginRedirectMap.get(platformType);
                redirectUri = URLEncoder.encode(RedisUtil.getProperty(loginRedirectKey, CORP_ID.toString()), ENCODE);
            }
        } catch (UnsupportedEncodingException ue) {
            logger.error("不支持的编码格式，编码接口URI失败：{}", ue.getMessage());
        }
        EnumMap<AuthUrlParam, String> authParam = new EnumMap<>(AuthUrlParam.class);
        authParam.put(AuthUrlParam.WEB_APP_ID, RedisUtil.getProperty(appIdKey, CORP_ID.toString()));
        authParam.put(AuthUrlParam.AUTH_URL_PREFIX, RedisUtil.getProperty(prefixKey, CORP_ID.toString()));
        authParam.put(AuthUrlParam.REDIRECT_URI, redirectUri);
        // 授权URL随机字符串
        String state = UUID.randomUUID().toString().replace("-", "") + "_" + dto.getAppType();
        authParam.put(AuthUrlParam.STATE, state);
        this.setAndExpiredOptional(state, state, EXPIRE_TIME);
        return authParam;
    }

    /**
     * 获取请求QQToken接口请求参数
     *
     * @author 曲元涛
     * @date 2020/7/10 14:43
     * @param code      访问凭证
     * @param isLogin   是否登录操作
     */
    private String getQQAuthTokenParam(String code, boolean isLogin) {
        String param = "";
        try {
            String appId = RedisUtil.getProperty("qqWebAppId", CORP_ID.toString());
            String appKey = RedisUtil.getProperty("qqWebAppKey", CORP_ID.toString());
            String uri = RedisUtil.getProperty(isLogin ? "qqLoginRedirectUri" : "qqBindRedirectUri", CORP_ID.toString());
            String redirectUri = URLEncoder.encode(uri, ENCODE);
            param = "grant_type=authorization_code&client_id=" + appId + "&client_secret=" + appKey
                    + "&redirect_uri=" + redirectUri + "&code=" + code;
        } catch (Exception e) {
            LCLogger.error().withMessage("getQQAuthUrl").withMessage(e.getMessage()).flush();
        }
        return param;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        appLoginTypeMap.put(PC_CRM, "crmThird");
        appLoginTypeMap.put(PC_DMS, "dmsThird");
        appLoginTypeMap.put(PC_STORE, "dmsThird");

        authUrlPrefixMap.put(PLATFORM_WX, "wxAuthUrlPrefix");
        authUrlPrefixMap.put(PLATFORM_QQ, "qqAuthUrlPrefix");

        webAppIdMap.put(PLATFORM_WX, "wxWebAppID");
        webAppIdMap.put(PLATFORM_QQ, "qqWebAppId");

        bindRedirectMap.put(PLATFORM_WX, "wxBindRedirectUri");
        bindRedirectMap.put(PLATFORM_QQ, "qqBindRedirectUri");

        loginRedirectMap.put(PLATFORM_WX, "wxLoginRedirectUri");
        loginRedirectMap.put(PLATFORM_QQ, "qqLoginRedirectUri");

        wxAppTypeToSecret.put(PC_CRM, Arrays.asList("wxWebAppID", "wxWebAppSecret"));
        wxAppTypeToSecret.put(PC_DMS, Arrays.asList("wxWebAppID", "wxWebAppSecret"));
        wxAppTypeToSecret.put(PC_STORE, Arrays.asList("wxWebAppID", "wxWebAppSecret"));
        wxAppTypeToSecret.put(APP_MARKETING, Arrays.asList("wxMobileMarketingAppId", "wxMobileMarketingAppSecret"));
        wxAppTypeToSecret.put(APP_DMS, Arrays.asList("wxMobileDMSAppId", "wxMobileDMSAppSecret"));
    }
}

